import{_ as e,U as n,V as i,Y as s}from"./framework-65d5b469.js";const a={},l=s(`<h1 id="组件通信" tabindex="-1"><a class="header-anchor" href="#组件通信" aria-hidden="true">#</a> 组件通信</h1><h2 id="props" tabindex="-1"><a class="header-anchor" href="#props" aria-hidden="true">#</a> Props</h2><p>父传子</p><div class="language-textmate line-numbers-mode" data-ext="textmate"><pre class="language-textmate"><code>&lt;template&gt;
  &lt;Child :msg=&quot;message&quot; /&gt; &lt;!-- 使用子组件 --&gt;
&lt;/template&gt;

&lt;script setup&gt;
import Child from &#39;./components/Child.vue&#39; // 引入子组件
let message = &#39;雷猴&#39;
&lt;/script&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><div class="language-textmate line-numbers-mode" data-ext="textmate"><pre class="language-textmate"><code>&lt;template&gt;
  &lt;div&gt;{{ msg }}&lt;/div&gt;
&lt;/template&gt;

&lt;script setup&gt; 
const props = defineProps({
  msg: {
    type: String,
    default: &#39;&#39;
  }
})
console.log(props.msg) // 在 js 里需要使用 props.xxx 的方式使用。在 html 中使用不需要 props
&lt;/script&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>说明</p><div class="language-text line-numbers-mode" data-ext="text"><pre class="language-text"><code>在&lt;script setup&gt; 中必须使用 defineProps API 来声明 props，
它具备完整的推断并且在 &lt;script setup&gt; 中是直接可用的。
在 &lt;script setup&gt; 中，defineProps 不需要另外引入。
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="emits" tabindex="-1"><a class="header-anchor" href="#emits" aria-hidden="true">#</a> emits</h2><p>子组件通知父组件触发一个事件，并且可以传值给父组件。（子传父）</p><div class="language-textmate line-numbers-mode" data-ext="textmate"><pre class="language-textmate"><code>&lt;template&gt;
  &lt;div&gt;父组件：{{ message }}&lt;/div&gt;
  &lt;Child @changeMsg=&quot;changeMessage&quot; /&gt; &lt;!-- 自定义 changeMsg 事件 --&gt;
&lt;/template&gt;

&lt;script setup&gt;
import { ref } from &#39;vue&#39;
import Child from &#39;./components/Child.vue&#39;
let message = ref(&#39;雷猴&#39;)

function changeMessage(data) { //更改message的值 data是从子组件传过来的
  message.value = data
}
&lt;/script&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><div class="language-textmate line-numbers-mode" data-ext="textmate"><pre class="language-textmate"><code>&lt;template&gt;
  &lt;div&gt;
    子组件：&lt;button @click=&quot;handleClick&quot;&gt;子组件的按钮&lt;/button&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script setup&gt;
// 注册一个自定义事件名，向上传递时告诉父组件要触发的事件。
const emit = defineEmits([&#39;changeMsg&#39;])

function handleClick() {
  // 参数1：事件名
  // 参数2：传给父组件的值
  emit(&#39;changeMsg&#39;, &#39;鲨鱼辣椒&#39;)
}
&lt;/script&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>说明</p><div class="language-text line-numbers-mode" data-ext="text"><pre class="language-text"><code>和 props 一样，在 &lt;script setup&gt; 中必须使用 defineEmits API 来声明 emits，
它具备完整的推断并且在 &lt;script setup&gt; 中是直接可用的。
在 &lt;script setup&gt; 中，defineEmits 不需要另外引入。
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="expose-ref" tabindex="-1"><a class="header-anchor" href="#expose-ref" aria-hidden="true">#</a> expose / ref</h2><p>子组件可以通过 expose 暴露自身的方法和数据。 父组件通过 ref 获取到子组件并调用其方法或访问数据。</p><div class="language-textmate line-numbers-mode" data-ext="textmate"><pre class="language-textmate"><code>&lt;template&gt;
  &lt;div&gt;父组件：拿到子组件的message数据：{{ msg }}&lt;/div&gt;
  &lt;button @click=&quot;callChildFn&quot;&gt;调用子组件的方法&lt;/button&gt;

  &lt;Child ref=&quot;com&quot; /&gt;
&lt;/template&gt;

&lt;script setup&gt;
import { ref, onMounted } from &#39;vue&#39;
import Child from &#39;./components/Child.vue&#39;

const com = ref(null) // 通过 模板ref 绑定子组件
const msg = ref(&#39;&#39;)

onMounted(() =&gt; {
  // 在加载完成后，将子组件的 message 赋值给 msg
  msg.value = com.value.message
})

function callChildFn() {
  // 调用子组件的 changeMessage 方法
  com.value.changeMessage(&#39;蒜头王八&#39;)

  // 重新将 子组件的message 赋值给 msg
  msg.value = com.value.message
}
&lt;/script&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><div class="language-textmate line-numbers-mode" data-ext="textmate"><pre class="language-textmate"><code>&lt;template&gt;
  &lt;div&gt;子组件：{{ message }}&lt;/div&gt;
&lt;/template&gt;

&lt;script setup&gt;
import { ref } from &#39;vue&#39;

const message = ref(&#39;蟑螂恶霸&#39;)
function changeMessage(data) {
  message.value = data
}

//使用 defineExpose 向外暴露指定的数据和方法
defineExpose({
  message,
  changeMessage
})
&lt;/script&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="non-props-attrs" tabindex="-1"><a class="header-anchor" href="#non-props-attrs" aria-hidden="true">#</a> Non-Props($attrs)</h2><div class="language-text line-numbers-mode" data-ext="text"><pre class="language-text"><code>所谓的 Non-Props 就是 非 Prop 的 Attribute。
意思是在子组件中，没使用 prop 或 emits 定义的 attribute，可以通过 $attrs 来访问。
常见的有 class 、style 和 id。
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="单个根元素的情况" tabindex="-1"><a class="header-anchor" href="#单个根元素的情况" aria-hidden="true">#</a> 单个根元素的情况</h3><div class="language-textmate line-numbers-mode" data-ext="textmate"><pre class="language-textmate"><code>&lt;template&gt;
  &lt;Child msg=&quot;雷猴 世界！&quot; name=&quot;鲨鱼辣椒&quot; /&gt;
&lt;/template&gt;

&lt;script setup&gt;
import { ref } from &#39;vue&#39;
import Child from &#39;./components/Child.vue&#39;
&lt;/script&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><div class="language-textmate line-numbers-mode" data-ext="textmate"><pre class="language-textmate"><code>&lt;template&gt;
  &lt;div&gt;子组件：打开控制台看看&lt;/div&gt;
&lt;/template&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>渲染结果</p><div class="language-text line-numbers-mode" data-ext="text"><pre class="language-text"><code>&lt;div msg=&quot;雷猴 世界!&quot;name=&quot;鲨鱼辣椒&quot;&gt;子组件：打开控制台看看&lt;div&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div></div></div><h3 id="多个根元素的情况" tabindex="-1"><a class="header-anchor" href="#多个根元素的情况" aria-hidden="true">#</a> 多个根元素的情况</h3><p>在 Vue3 中，组件已经没规定只能有一个根元素了。 如果子组件是多个元素时，上面的例子就不生效了</p><div class="language-textmate line-numbers-mode" data-ext="textmate"><pre class="language-textmate"><code>&lt;template&gt;
  &lt;div&gt;子组件：打开控制台看看&lt;/div&gt;
  &lt;div&gt;子组件：打开控制台看看&lt;/div&gt;
&lt;/template&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>解决方法：可以使用 $attrs 的方式进行绑定</p><div class="language-textmate line-numbers-mode" data-ext="textmate"><pre class="language-textmate"><code>&lt;template&gt;
  &lt;div :message=&quot;$attrs.msg&quot;&gt;只绑定指定值&lt;/div&gt;
  &lt;div v-bind=&quot;$attrs&quot;&gt;全绑定&lt;/div&gt;
&lt;/template&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>渲染结果</p><div class="language-text line-numbers-mode" data-ext="text"><pre class="language-text"><code>&lt;div mes5age=&quot;雷猴世界&quot;&gt;只定指定值&lt;div&gt;
&lt;div msg=&quot;雷猴世界&quot; name=&quot;鲨鱼辣椒&quot;&gt;全绑定&lt;/div&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="v-model" tabindex="-1"><a class="header-anchor" href="#v-model" aria-hidden="true">#</a> v-model</h2><h3 id="单个v-model写法" tabindex="-1"><a class="header-anchor" href="#单个v-model写法" aria-hidden="true">#</a> 单个v-model写法</h3><p>组件上的 v-model 使用 modelValue 作为 prop 和 update:modelValue 作为事件</p><div class="language-textmate line-numbers-mode" data-ext="textmate"><pre class="language-textmate"><code>&lt;template&gt;
  &lt;Child v-model=&quot;message&quot; /&gt;
&lt;/template&gt;

&lt;script setup&gt;
import { ref } from &#39;vue&#39;
import Child from &#39;./components/Child.vue&#39;
const message = ref(&#39;雷猴&#39;)
&lt;/script&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><div class="language-textmate line-numbers-mode" data-ext="textmate"><pre class="language-textmate"><code>&lt;template&gt;
  &lt;div @click=&quot;handleClick&quot;&gt;{{modelValue}}&lt;/div&gt;
&lt;/template&gt;

&lt;script setup&gt;
import { ref } from &#39;vue&#39;

const props = defineProps([
  &#39;modelValue&#39; // 接收父组件使用 v-model 传进来的值，必须用 modelValue 这个名字来接收
])

const emit = defineEmits([&#39;update:modelValue&#39;]) // 必须用 update:modelValue 这个名字来通知父组件修改值
function handleClick() {
  // 参数1：通知父组件修改值的方法名
  // 参数2：要修改的值
  emit(&#39;update:modelValue&#39;, &#39;喷射河马&#39;)
}
&lt;/script&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>更简单写法</p><div class="language-textmate line-numbers-mode" data-ext="textmate"><pre class="language-textmate"><code>&lt;template&gt;
  &lt;div @click=&quot;$emit(&#39;update:modelValue&#39;, &#39;喷射河马&#39;)&quot;&gt;{{modelValue}}&lt;/div&gt;
&lt;/template&gt;

&lt;script setup&gt;
import { ref } from &#39;vue&#39;

const props = defineProps([
  &#39;modelValue&#39; // 接收父组件使用 v-model 传进来的值，必须用 modelValue 这个名字来接收
])
&lt;/script&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="多个v-model绑定" tabindex="-1"><a class="header-anchor" href="#多个v-model绑定" aria-hidden="true">#</a> 多个v-model绑定</h3><div class="language-textmate line-numbers-mode" data-ext="textmate"><pre class="language-textmate"><code>&lt;template&gt;
  &lt;Child v-model:msg1=&quot;message1&quot; v-model:msg2=&quot;message2&quot; /&gt;
&lt;/template&gt;

&lt;script setup&gt;
import { ref } from &#39;vue&#39;
import Child from &#39;./components/Child.vue&#39;

const message1 = ref(&#39;雷猴&#39;)
const message2 = ref(&#39;蟑螂恶霸&#39;)
&lt;/script&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><div class="language-textmate line-numbers-mode" data-ext="textmate"><pre class="language-textmate"><code>&lt;template&gt;
  &lt;div&gt;&lt;button @click=&quot;changeMsg1&quot;&gt;修改msg1&lt;/button&gt; {{msg1}}&lt;/div&gt;
  &lt;div&gt;&lt;button @click=&quot;changeMsg2&quot;&gt;修改msg2&lt;/button&gt; {{msg2}}&lt;/div&gt;
&lt;/template&gt;

&lt;script setup&gt;
import { ref } from &#39;vue&#39;

const props = defineProps({ //接收
  msg1: String,
  msg2: String
})

const emit = defineEmits([&#39;update:msg1&#39;, &#39;update:msg2&#39;])

function changeMsg1() {
  emit(&#39;update:msg1&#39;, &#39;鲨鱼辣椒&#39;)
}

function changeMsg2() {
  emit(&#39;update:msg2&#39;, &#39;蝎子莱莱&#39;)
}
&lt;/script&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="v-model修饰符" tabindex="-1"><a class="header-anchor" href="#v-model修饰符" aria-hidden="true">#</a> v-model修饰符</h3><p>v-model 还能通过 . 的方式传入修饰。</p><div class="language-textmate line-numbers-mode" data-ext="textmate"><pre class="language-textmate"><code>&lt;template&gt;
  &lt;Child v-model.uppercase=&quot;message&quot; /&gt;
&lt;/template&gt;

&lt;script setup&gt;
import { ref } from &#39;vue&#39;
import Child from &#39;./components/Child.vue&#39;

const message = ref(&#39;hello&#39;)
&lt;/script&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><div class="language-textmate line-numbers-mode" data-ext="textmate"><pre class="language-textmate"><code>&lt;template&gt;
  &lt;div&gt;{{modelValue}}&lt;/div&gt;
&lt;/template&gt;

&lt;script setup&gt;
import { ref, onMounted } from &#39;vue&#39;

const props = defineProps([
  &#39;modelValue&#39;,
  &#39;modelModifiers&#39;
])

const emit = defineEmits([&#39;update:modelValue&#39;])

onMounted(() =&gt; {
  // 判断有没有 uppercase 修饰符，有的话就执行 toUpperCase() 方法
  if (props.modelModifiers.uppercase) {
    emit(&#39;update:modelValue&#39;, props.modelValue.toUpperCase())
  }
})
&lt;/script&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="插槽-slot" tabindex="-1"><a class="header-anchor" href="#插槽-slot" aria-hidden="true">#</a> 插槽 slot</h2><h2 id="provide-inject" tabindex="-1"><a class="header-anchor" href="#provide-inject" aria-hidden="true">#</a> provide / inject</h2><div class="language-text line-numbers-mode" data-ext="text"><pre class="language-text"><code>遇到多层传值时，使用 props 和 emit 的方式会显得比较笨拙。这时就可以用 provide 和 inject 了。
provide 是在父组件里使用的，可以往下传值。
inject 是在子(后代)组件里使用的，可以往上取值。
无论组件层次结构有多深，父组件都可以作为其所有子组件的依赖提供者。

provide 可以配合 readonly 一起使用
provide 和 inject 其实主要是用在深层关系中传值
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><div class="language-textmate line-numbers-mode" data-ext="textmate"><pre class="language-textmate"><code>&lt;template&gt;
  &lt;Child&gt;&lt;/Child&gt;
&lt;/template&gt;

&lt;script setup&gt;
import { ref, provide, readonly } from &#39;vue&#39;
import Child from &#39;./components/Child.vue&#39;

const name = ref(&#39;猛虎下山&#39;)
const msg = ref(&#39;雷猴&#39;)

// 使用readonly可以让子组件无法直接修改，需要调用provide往下传的方法来修改
provide(&#39;name&#39;, readonly(name))
provide(&#39;msg&#39;, msg)

provide(&#39;changeName&#39;, (value) =&gt; {
  name.value = value
})
&lt;/script&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><div class="language-textmate line-numbers-mode" data-ext="textmate"><pre class="language-textmate"><code>&lt;template&gt;
    &lt;div&gt;msg: {{ msg }}&lt;/div&gt;
    &lt;div&gt;name: {{name}}&lt;/div&gt;
    &lt;button @click=&quot;handleClick&quot;&gt;修改&lt;/button&gt;
&lt;/template&gt;

&lt;script setup&gt;
import { inject } from &#39;vue&#39;

const name = inject(&#39;name&#39;, &#39;hello&#39;) // 看看有没有值，没值的话就适用默认值（这里默认值是hello）
const msg = inject(&#39;msg&#39;)
const changeName = inject(&#39;changeName&#39;)

function handleClick() {
  // 这样写不合适，因为vue里推荐使用单向数据流，当父级使用readonly后，这行代码是不会生效的。没使用之前才会生效。
  // name.value = &#39;雷猴&#39;

  // 正确的方式
  changeName(&#39;虎躯一震&#39;)

  // 因为 msg 没被 readonly 过，所以可以直接修改值
  msg.value = &#39;世界&#39;
}
&lt;/script&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="总线-bus" tabindex="-1"><a class="header-anchor" href="#总线-bus" aria-hidden="true">#</a> 总线 bus</h2><div class="language-text line-numbers-mode" data-ext="text"><pre class="language-text"><code>在 Vue2 有总线传值的方法，我们在 Vue3 中也可以自己模拟。
这个方式其实有点像 Vuex 或者 Pinia 那样，弄一个独立的工具出来专门控制数据。
但和 Vuex 或 Pinia 相比，我们自己写的这个方法并没有很好的数据跟踪之类的特性。
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><div class="language-text line-numbers-mode" data-ext="text"><pre class="language-text"><code>原理
创建一个 Bus.js 文件，用来控制数据和注册事件的。
Bus.js 里有一个 Bus 类
eventList 是必须项，用来存放事件列表的。
constructor 里除了 eventList 外，其他都是自定义数据，公共数据就是存在这里的。
$on 方法用来注册事件。
$emit 方法可以调用 $on 里的事件。
$off 方法可以注销 eventList 里的事件。
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Bus.js</p><div class="language-javascript line-numbers-mode" data-ext="js"><pre class="language-javascript"><code><span class="token keyword">import</span> <span class="token punctuation">{</span> ref <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">&#39;vue&#39;</span>
<span class="token keyword">class</span> <span class="token class-name">Bus</span> <span class="token punctuation">{</span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 收集订阅信息,调度中心</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>eventList <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token comment">// 事件列表，这项是必须的</span>
    <span class="token comment">// 下面的都是自定义值</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>msg <span class="token operator">=</span> <span class="token function">ref</span><span class="token punctuation">(</span><span class="token string">&#39;这是一条总线的信息&#39;</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// 订阅</span>
  <span class="token function">$on</span><span class="token punctuation">(</span><span class="token parameter">name<span class="token punctuation">,</span> fn</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>eventList<span class="token punctuation">[</span>name<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>eventList<span class="token punctuation">[</span>name<span class="token punctuation">]</span> <span class="token operator">||</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>eventList<span class="token punctuation">[</span>name<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>fn<span class="token punctuation">)</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// 发布</span>
  <span class="token function">$emit</span><span class="token punctuation">(</span><span class="token parameter">name<span class="token punctuation">,</span> data</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>eventList<span class="token punctuation">[</span>name<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">this</span><span class="token punctuation">.</span>eventList<span class="token punctuation">[</span>name<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">fn</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
        <span class="token function">fn</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>

  <span class="token comment">// 取消订阅</span>
  <span class="token function">$off</span><span class="token punctuation">(</span><span class="token parameter">name</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>eventList<span class="token punctuation">[</span>name<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">delete</span> <span class="token keyword">this</span><span class="token punctuation">.</span>eventList<span class="token punctuation">[</span>name<span class="token punctuation">]</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">new</span> <span class="token class-name">Bus</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>父</p><div class="language-textmate line-numbers-mode" data-ext="textmate"><pre class="language-textmate"><code>&lt;template&gt;
  &lt;div&gt;
    &lt;span style=&quot;margin-right: 30px;&quot;&gt;message: {{ message }}&lt;/span&gt;
    &lt;span&gt;msg: {{ msg }}&lt;/span&gt;
  &lt;/div&gt;
  &lt;Child&gt;&lt;/Child&gt;
&lt;/template&gt;

&lt;script setup&gt;
import { ref } from &#39;vue&#39;
import Bus from &#39;./Bus.js&#39;
import Child from &#39;./components/Child.vue&#39;

const msg = ref(Bus.msg)

const message = ref(&#39;hello&#39;)

Bus.$on(&#39;changeMsg&#39;, data =&gt; { //用监听的写法
  message.value = data
})
&lt;/script&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><div class="language-textmate line-numbers-mode" data-ext="textmate"><pre class="language-textmate"><code>&lt;template&gt;
  &lt;div&gt;
    &lt;button @click=&quot;handleBusEmit&quot;&gt;触发Bus.$emit&lt;/button&gt;
    &lt;button @click=&quot;changeBusMsg&quot;&gt;修改总线里的 msg&lt;/button&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script setup&gt;
import Bus from &#39;../Bus.js&#39;

function handleBusEmit() {
  Bus.$emit(&#39;changeMsg&#39;, &#39;雷猴啊&#39;)
}

function changeBusMsg() {
  Bus.msg.value = &#39;在子组件里修改了总线的值&#39;
}
&lt;/script&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="getcurrentinstance" tabindex="-1"><a class="header-anchor" href="#getcurrentinstance" aria-hidden="true">#</a> getCurrentInstance</h2><div class="language-text line-numbers-mode" data-ext="text"><pre class="language-text"><code>getcurrentinstance 是 vue 提供的一个方法，支持访问内部组件实例。
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div></div></div><div class="hint-container tip"><p class="hint-container-title">提示</p><ul><li>getCurrentInstance 只暴露给高阶使用场景，典型的比如在库中。</li><li>强烈反对在应用的代码中使用 getCurrentInstance。</li><li>请不要把它当作在组合式 API 中获取 this 的替代方案来使用。</li><li>这个方法 适合在开发组件库的情况下使用，不适合日常业务开发中使用。</li><li>getCurrentInstance 只能在 setup 或生命周期钩子中调用</li></ul></div><div class="language-text line-numbers-mode" data-ext="text"><pre class="language-text"><code>在 &lt;script setup&gt; 中，我模拟了类似 $parent 和 $children 的方式。
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div></div></div><div class="language-textmate line-numbers-mode" data-ext="textmate"><pre class="language-textmate"><code>&lt;template&gt;
  &lt;div&gt;父组件 message 的值: {{ message }}&lt;/div&gt;
  &lt;button @click=&quot;handleClick&quot;&gt;获取子组件&lt;/button&gt;
  &lt;Child&gt;&lt;/Child&gt;
  &lt;Child&gt;&lt;/Child&gt;
&lt;/template&gt;

&lt;script setup&gt;
import { ref, getCurrentInstance, onMounted } from &#39;vue&#39;
import Child from &#39;./components/Child.vue&#39;

const message = ref(&#39;雷猴啊&#39;)

let instance = null

onMounted(() =&gt; {
  instance = getCurrentInstance()
})

// 子组件列表
let childrenList = []

// 注册组件
function registrationCom(com) {
  childrenList.push(com)
}

function handleClick() {
  if (childrenList.length &gt; 0) {
    childrenList.forEach(item =&gt; {
      console.log(&#39;组件实例：&#39;, item)
      console.log(&#39;组件名(name)：&#39;, item.type.name)
      console.log(&#39;组件输入框的值：&#39;, item.devtoolsRawSetupState.inputValue)
    })
  }
}
&lt;/script&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><div class="language-textmate line-numbers-mode" data-ext="textmate"><pre class="language-textmate"><code>&lt;template&gt;
  &lt;div&gt;
    子组件：&lt;button @click=&quot;handleClick&quot;&gt;获取父组件的值&lt;/button&gt;
    &lt;input type=&quot;text&quot; v-model=&quot;inputValue&quot;&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
export default {
  name: &#39;ccccc&#39;
}
&lt;/script&gt;

&lt;script setup&gt;
import { getCurrentInstance, onMounted, nextTick, ref } from &#39;vue&#39;

const inputValue = ref(&#39;&#39;)

let instance = null

onMounted(() =&gt; {
  instance = getCurrentInstance()
  nextTick(() =&gt; {
    instance.parent.devtoolsRawSetupState.registrationCom(instance)
  })

})

function handleClick() {
  let msg = instance.parent.devtoolsRawSetupState.message
  msg.value = &#39;哈哈哈哈哈哈&#39;
}
&lt;/script&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="vuex" tabindex="-1"><a class="header-anchor" href="#vuex" aria-hidden="true">#</a> Vuex</h2><h2 id="pinia" tabindex="-1"><a class="header-anchor" href="#pinia" aria-hidden="true">#</a> Pinia</h2><h2 id="mitt-js" tabindex="-1"><a class="header-anchor" href="#mitt-js" aria-hidden="true">#</a> mitt.js</h2><div class="language-text line-numbers-mode" data-ext="text"><pre class="language-text"><code>前面用到的 总线 Bus 方法，其实和 mitt.js 有点像，但 mitt.js 提供了更多的方法。

比如：
on：添加事件
emit：执行事件
off：移除事件
clear：清除所有事件

mitt.js 不是专门给 Vue 服务的，但 Vue 可以利用 mitt.js 做跨组件通信
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="安装" tabindex="-1"><a class="header-anchor" href="#安装" aria-hidden="true">#</a> 安装</h3><div class="language-text line-numbers-mode" data-ext="text"><pre class="language-text"><code>npm i mitt 
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div></div></div><h3 id="使用" tabindex="-1"><a class="header-anchor" href="#使用" aria-hidden="true">#</a> 使用</h3><p>在同级目录创建3个文件用作模拟。</p><div class="language-text line-numbers-mode" data-ext="text"><pre class="language-text"><code>Parent.vue
Child.vue
Bus.js
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Bus.js</p><div class="language-javascript line-numbers-mode" data-ext="js"><pre class="language-javascript"><code><span class="token keyword">import</span> mitt <span class="token keyword">from</span> <span class="token string">&#39;mitt&#39;</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token function">mitt</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div></div></div><p>Parent.vue</p><div class="language-textmate line-numbers-mode" data-ext="textmate"><pre class="language-textmate"><code>&lt;template&gt;
  &lt;div&gt;
    Mitt
    &lt;Child /&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script setup&gt;
import Child from &#39;./Child.vue&#39;
import Bus from &#39;./Bus.js&#39;

Bus.on(&#39;sayHello&#39;, () =&gt; console.log(&#39;雷猴啊&#39;))
&lt;/script&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Child.vue</p><div class="language-textmate line-numbers-mode" data-ext="textmate"><pre class="language-textmate"><code>&lt;template&gt;
  &lt;div&gt;
    Child：&lt;button @click=&quot;handleClick&quot;&gt;打声招呼&lt;/button&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script setup&gt;
import Bus from &#39;./Bus.js&#39;

function handleClick() {
  Bus.emit(&#39;sayHello&#39;)
}
&lt;/script&gt;
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><div class="language-text line-numbers-mode" data-ext="text"><pre class="language-text"><code>点击 Child.vue 上的按钮，在控制台就会执行在 Parent.vue 里定义的方法
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div></div></div>`,80),t=[l];function d(v,c){return n(),i("div",null,t)}const r=e(a,[["render",d],["__file","组件通信.html.vue"]]);export{r as default};
